<?php
/**
 * Coffee Framework

 * @category   CF
 * @package    CF_db
 * @author     그네 <Jung Sik, Park> <ccooffeee at hotmail dot com>
 * @copyright  Copyright (c) 2009 Jung Sik, Park <ccooffeee at hotmail dot com>
 * @license    doc/LICENSE    Based New BSD License
 * @version    $Id: db.php,v 0.1.3 2010/08/09 ccooffeee Exp $

 * LICENSE

 * 본 프로그램은 New BSD License 를 기본으로 하고 있지만 약간 다른 제약조건을 가지고 있습니다. 
 * 함께 제공된 license file 인  doc/LICENSE 를 꼭 확인하시길 바랍니다.

 * This Source is subject to the New BSD License with SOME CONSTRAINT that is boudled
 * with this package in the file doc/LICENSE 
 */


require_once __DIR__.'/abstract.php';


class CF_db_oracle extends CF_db_abstract 
{
// {{{ properties
	private $conn = null;
	private $_lobDp = array();
// }}}


// {{{ void begin ()
	public function begin () {
		$this->connect();
		if($this->transaction === true) return;

		$this->transaction = true;
	}
// }}}
// {{{ void commit ()
	public function commit () {
		if($this->transaction !== true) return;

		if(!oci_commit($this->conn))
			throw new CF_db_exception (oci_error($this->conn));

		$this->transaction = false; 
	}
// }}}
// {{{ void rollback ([string savepoint])
	public function rollback ($savepoint = null) {
		if($this->transaction !== true) return;

		if(!oci_rollback($this->conn))
			throw new CF_db_exception (oci_error($this->conn));

		$this->transaction = false; 
	}
// }}}

// {{{ CF_db_interface query (string sql, array param = array())
	public function query ($query, array $param = array()) {
		$this->connect();

		$this->free();

		$this->stmt = oci_parse($this->conn, $query);
		$err = oci_error($this->stmt);
		if($err)
			throw new CF_db_exception ($err);

		$param = $this->_bind($param);

		$trans = $this->transaction !== true && count($this->_lobDp) > 0 ? false : true;

		if(!$trans) $this->begin();
		if(!oci_execute($this->stmt, $this->getMode())) {
			$this->_lobFree();
			$this->rollback();
			throw new CF_db_exception (oci_error($this->stmt));
		}
		foreach($this->_lobDp as $key => $lob) {
			if(!$lob->write($param[$key]['data'])) {
				$this->_lobFree();
				$this->rollback();
				throw new CF_db_exception('Write data to Lob Error');
			}
		}

		if(!$trans) {
			$this->commit();
			$this->_lobFree();
		}

		return $this;

		// insert into tab (key, clob, blob)
		//	values ($key, empty_clob(), empty_blob())
		// returning clob, blob into :clob, :blob;
	}
// }}}
// {{{ mixed fetch ([string mode])
	public function fetch($mode = null) {
		if($this->stmt === null) return null;

		if($mode == 'array')
			$row = oci_fetch_assoc($this->stmt);
		else $row = oci_fetch_object($this->stmt);

		$err = oci_error($this->stmt);
		if($err) throw new CF_db_exception ($err);

		return $row;
	}
// }}}
// {{{ mixed fetchAll ([string mode])
	public function fetchAll ($mode = null) {
		if($this->stmt === null) return null;

		if($mode == 'array') {
			$flag = OCI_FETCHSTATEMENT_BY_ROW | OCI_ASSOC;
			oci_fetch_all($this->stmt, $result, 0, -1, $flag);
			if($err = oci_error($this->stmt))
				throw new CF_db_exception ($err);
		}
		else {
			$result = array();
			while($row = $this->fetch()) {
				$result[] = $row;
			}
		}

		return $result;
	}
// }}}
// {{{ string field ([integer col_index])
	public function field($index = 0) {
		if($this->stmt === null) return null;

		$row = oci_fetch_row($this->stmt);

		return $row[(int)$index];
	}
// }}}

// {{{ string readLob (OCI-Lob, boolean free = true)
	public function readLob ($lob, $free = true) {
		if(!is_object($lob) || !method_exists($lob, 'read'))
			return null;

		$data = $lob->read($lob->size());

		if($free === true) $lob->free();

		return $data;
	}
// }}}
// {{{ string insertId (string seq_name)
	public function insertId($name = null) {
		if($name === null) return null;

		$name = $this->escape($name);

		$query = 'SELECT '.$name.'.CURRVAL FROM DUAL';
		return $this->query($query)->field();
	}

// }}}
// {{{ int rowCount ()
	public function rowCount() {
		if($this->stmt === null) return 0;

		return oci_num_rows($this->stmt);
	}
// }}}
// {{{ void lowerField
	public function lowerField() {
		$count = oci_num_fields($this->stmt);
		for ($i = 1; $i <= $count; $i++) {
			$name = oci_field_name($this->stmt, $i);
			oci_define_by_name($this->stmt, $name, ${strtolower($name)});
		}
	}
// }}}
// {{{ void free ()
	public function free() {
		if($this->stmt) {
			$this->_lobFree();
			oci_free_statement($this->stmt);
		}
	}
// }}}

// {{{ bool isConnect ()
	public function isConnect() {
		return is_resource($this->conn)
				&& get_resource_type($this->conn) == 'oci8 connection';
	}
// }}}
// {{{ void close ()
	public function close() {
		if($this->isConnect())
			oci_close($this->conn);

		$this->conn = null;
    }
// }}}

// {{{ string escape (string)
	public function escape ($str) {
		if($this->_isQuoted())
			$str = stripslashes($str);

		$str = str_replace("'", "''", $str);
		$str = addcslashes($str, "\000\n\r\\\032");
        return $str;
	}
// }}}


// {{{ protected methods
	// {{{ boolean exec (string sql)
	protected function exec($query)
	{
		return oci_execute(oci_parse($this->conn, $query),
							$this->getMode());
    }
	// }}}
	// {{{ void connect () {
	protected function connect() {
		if(is_resource($this->conn)) return;
		if($this->config === null) return;

        if (!extension_loaded('oci8'))
			throw new CF_db_exception ('OCI8 extension is required');


		$tns = $this->configToDsn();

		//	NLS_LANG=KOREAN_KOREA.KO16MSWIN949
		//	$enc = $this->config['encoding'] ? $this->config['encoding'] : 'KO16KSC5601';
		$enc = $this->config['encoding'] ? $this->config['encoding'] : 'KO16MSWIN949';

		$conn = oci_new_connect($this->config['user'], $this->config['password'], $tns, $this->config['encoding']);

        if(!$conn) throw new CF_db_exception(oci_error());

        $this->conn = $conn;
	}
	// }}}
	// {{{ string configToDsn ()
	protected function configToDsn() {
		if($this->config === null) return null;

		if(!isset($this->config['driver']) || $this->config['driver'] != 'oracle')
			return null;

		$port = $this->config['port'] ? $this->config['port'] : 1521;

        $tns = "(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST = ".$this->config['server'].")(PORT = ".$port."))
               (CONNECT_DATA=(SERVER=DEDICATED)
               (SERVICE_NAME = ".$this->config['name'].")))";

        return $tns;
	}
	// }}}
    // {{{ void _bind (array param)
    protected function _bind (array $bind = array()) {
		$bind = parent::_bind($bind);

    	foreach($bind as $key => $val) {
			// oci_bind_by_name으로 key, val값 셋팅 할때 꼭 trim을 사용해야됩니다.
			// 여기서 key, val trim하면 안됩니다 -_-+;
			// 꼭 oci_bind_by_name 함수에 값 셋팅할때 trim 해야됨.
			
			if(is_array($val)) {
				if(!isset($val['type'])) $val['type'] = 'clob';
				if(!isset($val['data'])) $val['data'] = '';

				switch($val['type']) {
					case 'clob': $flag = OCI_B_CLOB; break;
					case 'blob': $flag = OCI_B_BLOB; break;
					default:
						$this->_lobFree();
						throw new CF_db_exception('Unknown Lob type');
				}
				$this->_lobDp[$key] = oci_new_descriptor($this->conn, OCI_DTYPE_LOB);

				if(!oci_bind_by_name($this->stmt, trim($key), trim($this->_lobDp[$key]), -1, $flag)) {
					$this->_lobFree();
					throw new CF_db_exception(oci_error($this->stmt));
				}
			}
			else {
				if(!oci_bind_by_name($this->stmt, trim($key), trim($val))) 
					throw new CF_db_exception(oci_error($this->stmt));
			}
		}

		return $bind;
    }
    // }}}
// }}}

// {{{ private methods
	// {{{ integer getMode ()
	private function getMode() {
		return $this->transaction ? OCI_DEFAULT : OCI_COMMIT_ON_SUCCESS;
	}
	// }}}
	// {{{ void _lobFree ()
	private function _lobFree() {
		foreach($this->_lobDp as $lob)
			$lob->free();
		$this->_lobDp = array();
	}
	// }}}
// }}}
}

// vim: ts=4 sw=4 fdm=marker
?>
